package chair;

import chair.core.Stimulus;
import chair.core.Stimulus.StimulusType;

/**
 * 
 * Un comportement permet d'occuper une fonction du robot quand il n'y aucun
 * réflexe en cours. Le faire explorer l'environnement, allumer telle lumière,
 * remuer la tête, etc. Chapeauté par le thread Striatum, ils sont choisis
 * aléatoirement par leurs fonctions. Sauf si on leur associe un stimulus,
 * auquel cas le modèle pourra trouver des correspondances avec des punitions ou
 * des récompenses et ainsi favoriser ou éviter tel comportement. Note : s'il
 * n'y a que deux comportements, avec une transition instantanée, alors les
 * définir comme non spontanés entraînerait une punition/récompense à cheval
 * sur la fin de l'un et le début de l'autre.
 * 
 * 
 * @author nomhad
 * 
 */
public abstract class Behavior {
	/** Nom du comportement */
	protected String _name;
	/** À quelle Function est rattaché ce comportement */
	private Function _linkedFunction;
	/**
	 * Est-ce que ce comportement peut être interrompu par un autre ou doit
	 * aller jusqu'à son terme ? (un réflexe aura la priorité)
	 */
	private boolean _stoppable = true;
	/**
	 * Stimulus associé. À la classe fille de lancer initStimulus() si veut
	 * permettre conditionnement opérant
	 */
	private Stimulus _stim = null;
	/**
	 * Pendant de l'attribut éponyme de Organ. Si false alors le comportement
	 * aura une durée pour le modèle, c'est à dire que Function enverra un front
	 * descendant lorsqu'il s'arrête et que Function s'appuiera à la fois sur
	 * les fronts montants et descendants pour calculer la récompense associée.
	 */
	private boolean _spontaneous = true;
	/**
	 * Est-ce que le comportement est encore d'actualité ? Ne sera pas
	 * sélectionné par la fonction si false
	 */
	private boolean _relevant = true;

	/**
	 * 
	 * Un comportement doit nécessairement être relié à une fonction. Il faut
	 * définir à l'initialisation si on veut associer un stimulus à ce
	 * comportement ou s'il n'aura aucun lien avec le modèle de conditionnement
	 * 
	 * @param fct
	 *            fonction associée
	 * @param name
	 *            nom associé
	 * @param setStimulus
	 *            true pour initialiation de _stim, false sinon
	 */
	public Behavior(Function fct, String name, boolean setStimulus) {
		plugToFunction(fct);
		_name = name;
		if (setStimulus) {
			// TODO: faudrait pas qu'ailleurs on définisse le même nom de
			// stimulus
			_stim = new Stimulus(_name + "-BehaviorStimulus", StimulusType.CS);
		}
	}

	/**
	 * Function désire savoir s'il peut se contenter de ne jongler qu'avec le
	 * front montant.
	 * 
	 * @return true si seul le front montant compte, false sinon
	 */
	final boolean isSpontaneous() {
		return _spontaneous;
	}

	/**
	 * Une classe fille peut décider d'avoir une dimension temporelle. Surtout
	 * utile si le comportement met un certain temps à s'accomplir (supérieur à
	 * Parameters.deltaMax) et qu'on ne le sanctionne qu'à la fin.
	 * 
	 * Exemple : "Tu viens d'arriver dans la chambre ? Pas bien !"
	 */
	final protected void setSpontaneous(boolean toggle) {
		_spontaneous = toggle;
	}

	/**
	 * getter de _stoppable
	 * 
	 * @return true si ce comportement peut être interrompu par un autre, false
	 *         si sont action doit être menuée jusqu'à son terme
	 */
	final boolean isStoppable() {
		return _stoppable;
	}

	/**
	 * setter de _stoppable
	 * 
	 * @param stoppable
	 *            nouvelle valeur du drapeau
	 *            "c'est bon les gars, vous pouvez me mettre au placard"
	 */
	final protected void setStoppable(boolean stoppable) {
		_stoppable = stoppable;
	}

	/**
	 * Relie le présent réflexe à sa fonction
	 * 
	 * @param fct
	 *            fonction à laquelle est relié le réflexe
	 */
	private final void plugToFunction(Function fct) {
		_linkedFunction = fct;
	}

	/**
	 * Accesseur utilisé dans Brain.addBehavior()
	 * 
	 * @return fonction associée
	 */
	final Function getFunction() {
		return _linkedFunction;
	}

	/**
	 * Retourne le stimulus assodié à ce comportement. Utilisé par Striatum pour
	 * s'enregistrer auprès du modèle et faire les prédictions sur le
	 * comportement à choisir.
	 * 
	 * @return Stimulus en question
	 */
	final Stimulus getStimulus() {
		return _stim;
	}

	/**
	 * On lance ou on arrête le comportement via booléen toggle
	 */
	protected abstract void run(boolean toggle);

	/**
	 * Permet à Function de faire tourner comportement en cours afin qu'il se
	 * mette à jour. La classe fille devra redéfinir cette méthode si utilise
	 * _stoppable = false afin de se signaler comme disponible. Par défaut ne
	 * fait rien.
	 * 
	 * @param time
	 *            temps actuel
	 */
	protected void tick(long time) {
	}

	/**
	 * Est-ce que le comportement est toujours d'actualité ?
	 * 
	 * @return false si s'est déclaré comme ne pouvant plus être sélectionnable
	 */
	boolean isRelevant() {
		return _relevant;
	}

	/**
	 * Déclare si le comportement mérite qu'on lui porte attention ou non.
	 * Attention, si le drapeau est modifié depuis l'extérieur il se peut qu'il
	 * ne soit pas pris en compte immédiatement (si vraiment pas de chance :
	 * Function est en train de choisir prochain comportement).
	 * 
	 * @param toggle
	 *            false s'il est inutile que Function le sélectionne, true sinon
	 *            (défaut)
	 */
	public void setRelvevant(boolean toggle) {
		_relevant = toggle;
	}

	@Override
	public String toString() {
		return "Behavior [_name=" + _name + ", _relevant=" + _relevant
				+ ", _spontaneous=" + _spontaneous + ", _stoppable="
				+ _stoppable + "]";
	}
}
